package com.xyy.saas.payment.payment.core.service;

import com.alibaba.fastjson.JSON;
import com.xyy.saas.payment.adpater.RouteManager;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.HolidayService;
import com.xyy.saas.payment.common.PinganCreditConstant;
import com.xyy.saas.payment.common.enums.AmountTypeEnum;
import com.xyy.saas.payment.cores.bo.QueryResultBo;
import com.xyy.saas.payment.cores.enums.BusinessIdTypeEnum;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.SeparateOrder;
import com.xyy.saas.payment.merchant.core.dto.QueryTradeStatusDto;
import com.xyy.saas.payment.merchant.core.dto.QueryTradeStatusVo;
import com.xyy.saas.payment.merchant.core.dto.QueryTransactionStatusDto;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.service.SeparateOrderService;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author wangzeqi
 */
@Slf4j
@Service
@DependsOn({"apolloUtil"})
@RequiredArgsConstructor
public class PinganCreditReconcileService {

    @Value("${pinganCredit.reconcile.startTime:}")
    private String pinganCreditReconcileStartTime;
    @Value("${pinganCredit.reconcile.endTime:}")
    private String pinganCreditReconcileEndTime;
    private final AlarmService alarmService;
    private final PayResultOrderService payResultOrderService;
    private final RefundOrderService refundOrderService;
    private final SeparateOrderService separateOrderService;
    private final HolidayService holidayService;
    private final RouteManager routeManager;
    private final PayAccountService payAccountService;

    /**
     * 平安贷支付
     */
    public void verifyCreditPaySettle() {
        if (holidayService.isHoliday(LocalDate.now())) {
            log.info("PinganCreditReconcileService.verifyCreditPaySettle no processing on holidays");
            return;
        }
        LocalDate nearestPrevWorkday = holidayService.getNearestPrevWorkday(LocalDate.now());
        LocalDateTime beginTime = nearestPrevWorkday.atStartOfDay();
        LocalDateTime endTime = LocalDate.now().atStartOfDay();
        if (StringUtils.isNotBlank(pinganCreditReconcileStartTime) && StringUtils.isNotBlank(pinganCreditReconcileEndTime)) {
            beginTime = LocalDateTime.parse(pinganCreditReconcileStartTime, DateUtil.PATTERN_DATE);
            endTime = LocalDateTime.parse(pinganCreditReconcileEndTime, DateUtil.PATTERN_DATE);
        }
        List<PayResultOrder> payResultOrders = payResultOrderService.queryPinganCreditListByTime(beginTime, endTime);
        if (CollectionUtils.isEmpty(payResultOrders)) {
            log.info("PinganCreditReconcileService.verifyCreditPaySettle payResultOrders is empty");
            return;
        }

        // 缺少分账记录
        final List<String> missingSeparateOrderPayNos = new ArrayList<>();
        // 分账记录状态不正确
        final List<String> invalidSeparateOrderStatusPayNos = new ArrayList<>();
        // 分账记录状态和平安不匹配
        final List<String> mismatchedPinganTradeStatusSeparateNos = new ArrayList<>();

        for (PayResultOrder payResultOrder : payResultOrders) {
            List<String> amountTypes;
            if (payResultOrder.getBusinessOrderType().contains(BusinessIdTypeEnum.EC_SELF_SUPPORT.getType())) {
                amountTypes = PinganCreditConstant.selfSupportCreditPayProcessAmountTypes;
            } else {
                amountTypes = PinganCreditConstant.unSelfSupportCreditPayProcessAmountTypes;
            }
            List<String> businessOrderNos = getSeparateOrderBusinessOrderNos(payResultOrder.getPayNo(), amountTypes);
            List<SeparateOrder> separateOrders = separateOrderService.selectListByBusinessOrderNos(businessOrderNos);
            if (CollectionUtils.isEmpty(separateOrders)) {
                // platformUnsettledAmount=0 no need settle
                continue;
            }
            if (separateOrders.size() < amountTypes.size()) {
                missingSeparateOrderPayNos.add(payResultOrder.getPayNo());
                continue;
            }
            boolean invalidSeparateOrderStatus = separateOrders.stream().anyMatch(s -> !SeparateOrder.STATUS_FINISH.equals(s.getStatus()));
            if (invalidSeparateOrderStatus) {
                invalidSeparateOrderStatusPayNos.add(payResultOrder.getPayNo());
                continue;
            }
            queryPinganCreditTradeStatusAndFillMismatched(payResultOrder.getBusinessOrderType(), payResultOrder.getPayNo(),
                    "", separateOrders, mismatchedPinganTradeStatusSeparateNos);
        }

        // alarm
        verifyCreditPaySettleAlarm(missingSeparateOrderPayNos, invalidSeparateOrderStatusPayNos, mismatchedPinganTradeStatusSeparateNos);
    }

    private void verifyCreditPaySettleAlarm(List<String> missingSeparateOrderPayNos, List<String> invalidSeparateOrderStatusPayNos,
                                            List<String> mismatchedPinganTradeStatusSeparateNos) {
        try {
            if (CollectionUtils.isNotEmpty(missingSeparateOrderPayNos)) {
                log.error("平安贷分账交易核对，缺少分账记录#payNos:{}", JSON.toJSONString(missingSeparateOrderPayNos));
                String payNos = String.join(",", missingSeparateOrderPayNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷分账交易核对")
                        .method("verifyCreditPaySettle")
                        .msg("平安贷分账异常，缺少分账记录，请尽快处理")
                        .param("payNos:" + payNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPaySettle missingSeparateOrderPayNos alarm error", e);
        }

        try {
            if (CollectionUtils.isNotEmpty(invalidSeparateOrderStatusPayNos)) {
                log.error("平安贷分账交易核对，分账记录状态不正确#payNos:{}", JSON.toJSONString(invalidSeparateOrderStatusPayNos));
                String payNos = String.join(",", invalidSeparateOrderStatusPayNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷分账交易核对")
                        .method("verifyCreditPaySettle")
                        .msg("平安贷分账异常，分账记录状态不正确，请尽快处理")
                        .param("payNos:" + payNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPaySettle invalidSeparateOrderStatusPayNos alarm error", e);
        }

        try {
            if (CollectionUtils.isNotEmpty(mismatchedPinganTradeStatusSeparateNos)) {
                log.error("平安贷分账交易核对，分账记录状态和平安不一致#separateNos:{}", JSON.toJSONString(mismatchedPinganTradeStatusSeparateNos));
                String separateNos = String.join(",", mismatchedPinganTradeStatusSeparateNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷分账交易核对")
                        .method("verifyCreditPaySettle")
                        .msg("平安贷分账异常，分账记录状态和平安不一致，请尽快处理")
                        .param("separateNos:" + separateNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPaySettle mismatchedPinganTradeStatusSeparateNos alarm error", e);
        }
    }

    @SneakyThrows
    private void queryPinganCreditTradeStatusAndFillMismatched(String businessOrderType, String payNo, String refundNo,
                                                               List<SeparateOrder> separateOrders, List<String> mismatchedSeparateNos) {
        for (SeparateOrder separateOrder : separateOrders) {
            String separateNo = separateOrder.getSeparateNo();
            String businessOrderNo = separateOrder.getBusinessOrderNo();
            String extra = separateOrder.getExtra();
            if (businessOrderNo.contains(AmountTypeEnum.PINGAN_CREDIT_PAY_ACCT_TRANS.name()) ||
                    businessOrderNo.contains(AmountTypeEnum.CREDIT_FREEZE.name()) ||
                    businessOrderNo.contains(AmountTypeEnum.PINGAN_CREDIT_PAY_ACCT_TRANS_REFUND.name()) ||
                    businessOrderNo.contains(AmountTypeEnum.CREDIT_UNFREEZE.name()) ||
                    Objects.equals(businessOrderNo, refundNo) && Objects.equals(extra, PinganCreditConstant.PINGAN_PAY_ACCT_TRANS)) {
                // 查交易状态
                QueryTradeStatusDto queryTradeStatusDto = QueryTradeStatusDto.builder().
                        businessIdType(businessOrderType).
                        accountId(separateOrder.getCustNoIn()).
                        tradeType(QueryTradeStatusDto.TRANSFER).
                        tradeNo(separateOrder.getSeparateNo()).
                        tradeDate(separateOrder.getCtime()).
                        build();
                QueryTradeStatusVo queryTradeStatusVo = routeManager.queryTradeStatus(queryTradeStatusDto);
                if (queryTradeStatusVo == null || !Objects.equals(QueryResultBo.FINISHED, queryTradeStatusVo.getStatus())) {
                    mismatchedSeparateNos.add(separateNo);
                }
            } else if (Objects.equals(businessOrderNo, payNo) && Objects.equals(extra, PinganCreditConstant.PINGAN_SETTLE) ||
                    businessOrderNo.contains(AmountTypeEnum.PINGAN_CREDIT_PAY_SETTLE_REFUND.name())) {
                // 查交易状态 退款，转账
                QueryTransactionStatusDto queryTransactionStatusDto = QueryTransactionStatusDto.build(
                        businessOrderType,
                        separateOrder.getSeparateNo(),
                        QueryTransactionStatusDto.TRADE_TYPE_TRANSFER,
                        separateOrder.getCustNoIn(),
                        separateOrder.getCtime());
                QueryResultBo queryResultBo = routeManager.queryTransactionStatus(queryTransactionStatusDto, Constants.CHANNEL_PINGAN_NAME);
                if (queryResultBo == null || !Objects.equals(QueryResultBo.FINISHED, queryResultBo.getStatus())) {
                    mismatchedSeparateNos.add(separateNo);
                }
            }
        }
    }

    private List<String> getSeparateOrderBusinessOrderNos(String orderNo, List<String> amountTypes) {
        return amountTypes.stream()
                .map(amountType -> StringUtils.isNotBlank(amountType) ? String.join("_", orderNo, amountType) : orderNo)
                .collect(Collectors.toList());
    }

    /**
     * 平安贷退款
     */
    public void verifyCreditPayRefund() {
        if (holidayService.isHoliday(LocalDate.now())) {
            log.info("PinganCreditReconcileService.verifyCreditPayRefund no processing on holidays");
            return;
        }
        LocalDate nearestPrevWorkday = holidayService.getNearestPrevWorkday(LocalDate.now());
        LocalDateTime beginTime = nearestPrevWorkday.atStartOfDay();
        LocalDateTime endTime = LocalDate.now().atStartOfDay();

        if (StringUtils.isNotBlank(pinganCreditReconcileStartTime) && StringUtils.isNotBlank(pinganCreditReconcileEndTime)) {
            beginTime = LocalDateTime.parse(pinganCreditReconcileStartTime, DateUtil.PATTERN_DATE);
            endTime = LocalDateTime.parse(pinganCreditReconcileEndTime, DateUtil.PATTERN_DATE);
        }
        List<RefundOrder> refundOrders = refundOrderService.queryPinganCreditListByTime(beginTime, endTime);
        if (CollectionUtils.isEmpty(refundOrders)) {
            log.info("PinganCreditReconcileService.verifyCreditPayRefund refundOrders is empty");
            return;
        }

        // 缺少分账记录
        final List<String> missingSeparateOrderRefundNos = new ArrayList<>();
        // 分账记录状态不正确
        final List<String> invalidSeparateOrderStatusRefundNos = new ArrayList<>();
        // 分账记录状态和平安不匹配
        final List<String> mismatchedPinganTradeStatusSeparateNos = new ArrayList<>();

        List<String> refundCreditPayProcessAmountTypes = PinganCreditConstant.refundCreditPayProcessAmountTypes;
        for (RefundOrder refundOrder : refundOrders) {
            if (isBigShop(refundOrder.getUserId())) {
                continue;
            }
            List<String> businessOrderNos = getSeparateOrderBusinessOrderNos(refundOrder.getRefundNo(), refundCreditPayProcessAmountTypes);
            List<SeparateOrder> separateOrders = separateOrderService.selectListByBusinessOrderNos(businessOrderNos);
            boolean selfSupportCreditRefund = refundOrder.getBusinessOrderType().contains(BusinessIdTypeEnum.EC_SELF_SUPPORT.getType());
            boolean popCreditRefund = BusinessIdTypeEnum.EC_POP.getType().equals(refundOrder.getBusinessOrderType());
            if (selfSupportCreditRefund && CollectionUtils.isEmpty(separateOrders)) {
                // 自营正向单未分账，可以为空
                continue;
            }
            if (popCreditRefund && CollectionUtils.isEmpty(separateOrders)) {
                missingSeparateOrderRefundNos.add(refundOrder.getRefundNo());
                continue;
            }
            if (separateOrders.size() < refundCreditPayProcessAmountTypes.size()) {
                missingSeparateOrderRefundNos.add(refundOrder.getRefundNo());
                continue;
            }
            if (separateOrders.stream().anyMatch(s -> !SeparateOrder.STATUS_FINISH.equals(s.getStatus()))) {
                invalidSeparateOrderStatusRefundNos.add(refundOrder.getRefundNo());
                continue;
            }
            queryPinganCreditTradeStatusAndFillMismatched(refundOrder.getBusinessOrderType(), refundOrder.getPayNo(),
                    refundOrder.getRefundNo(), separateOrders, mismatchedPinganTradeStatusSeparateNos);
        }

        // alarm
        verifyCreditPayRefundAlarm(missingSeparateOrderRefundNos, invalidSeparateOrderStatusRefundNos, mismatchedPinganTradeStatusSeparateNos);
    }

    private boolean isBigShop(String accountId) {
        return Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), accountId) ||
                Objects.equals(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_PINGAN_ACCOUNT_ID), accountId);
    }

    private void verifyCreditPayRefundAlarm(List<String> missingSeparateOrderRefundNos, List<String> invalidSeparateOrderStatusRefundNos,
                                            List<String> mismatchedPinganTradeStatusSeparateNos) {
        try {
            if (CollectionUtils.isNotEmpty(missingSeparateOrderRefundNos)) {
                log.error("平安贷退款交易核对，缺少分账记录#refundNos：{}", JSON.toJSONString(missingSeparateOrderRefundNos));
                String refundNos = String.join(",", missingSeparateOrderRefundNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷退款交易核对")
                        .method("verifyCreditPayRefund")
                        .msg("平安贷退款交易异常，缺少分账记录，请尽快处理")
                        .param("refundNos:" + refundNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPayRefund missingSeparateOrderRefundNos alarm error", e);
        }

        try {
            if (CollectionUtils.isNotEmpty(invalidSeparateOrderStatusRefundNos)) {
                log.error("平安贷退款交易核对，分账记录状态不正确#refundNos：{}", JSON.toJSONString(invalidSeparateOrderStatusRefundNos));
                String refundNos = String.join(",", invalidSeparateOrderStatusRefundNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷退款交易核对")
                        .method("verifyCreditPayRefund")
                        .msg("平安贷退款交易异常，分账记录状态不正确，请尽快处理")
                        .param("refundNos:" + refundNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPayRefund invalidSeparateOrderStatusRefundNos alarm error", e);
        }

        try {
            if (CollectionUtils.isNotEmpty(mismatchedPinganTradeStatusSeparateNos)) {
                log.error("平安贷退款交易核对，分账记录状态和平安不一致#separateNos：{}", JSON.toJSONString(mismatchedPinganTradeStatusSeparateNos));
                String separateNos = String.join(",", mismatchedPinganTradeStatusSeparateNos);
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("平安贷退款交易核对")
                        .method("verifyCreditPayRefund")
                        .msg("平安贷退款交易异常，分账记录状态和平安不一致，请尽快处理")
                        .param("separateNos:" + separateNos).build();
                alarmService.alarm(msg);
            }
        } catch (Exception e) {
            log.error("verifyCreditPayRefund mismatchedPinganTradeStatusSeparateNos alarm error", e);
        }
    }

    /**
     * 平安贷对账
     */
    public void executeReconcile() {

    }

}
